// 如果要在 video 上使用，可参考逻辑自行实现：
// 用 src 替换 aid, eid
// 用 tt.preloadResourceVideo 替换 tt.preloadVideo

/**
 * VideoSwiper 类
 * 负责处理视频播放器的滑动切换逻辑
 * 主要功能：
 * 1. 视频预加载机制
 * 2. 动态管理 swiper-item
 * 3. 处理边界情况（第一集、最后一集）
 * 4. 特殊剧集数处理（如总集数除以3余1或余2）
 */
export class VideoSwiper {
  // 所有剧集
  videoList = [];
  // 当前集，在 videoList 中的索引
  currentIndex = 0;
  // 上一集，在 videoList 中的索引，可能是 -1
  preIndex = 0;
  // 下一集，在 videoList 中的索引，可能是 videoList.length
  nextIndex = 0;
  // bindgetsource 时会赋值，是否已经获取到资源
  hasGotSource = [];
  // 缓存的 videoContext
  cacheVideoContext = [];
  // 是否能使用 bindgetsource 回调
  canUseBindGetsource = true;
  // 是否能使用 preloadResourceVideo 方法
  canUsePreloadResourceVideo = true;
  // swiper-item 列表
  curQueue = [null, null, null];
  // swiper 配置
  swiperOptions = {
    // 当前播放列表
    // 是否可以轮播，即尾部衔接头部，永为 true
    circular: false,
    duration: 500,
    current: 0,
  };

  // 传入配置
  options = {
    // swiper 的 duration
    swiperDuration: 500,
    // 开始集数，从 0 开始
    start: 0,
    // 总集数，从 1 开始
    total: 0,
    // 获取 video 函数，调用该函数可获取指定集
    // 返回的信息需包括以下字段：
    // 1. episode ，episode 字段从 0 开始，表示第几集，需以 data-episode 的方式，放到 video-player 组件上
    // 2. id，当前聚集的 id，唯一值，需要设置到 video-player 组件上
    // 3. aid，video-player 需要的参数
    // 4. eid，video-player 需要的参数
    // 5. 其他 video-player 所需参数
    getVideo: null,
    // 滑动到列表末尾执行，例如弹框：最后一集
    onEndList: null,
    // swiperOptions 变化时触发，需要 setData 到页面中
    onSwiperOptionsChange: null,
    // curQueue 变化时触发，需要 setData 到页面中，渲染 swiper-item
    // curQueue 的每一项会包括一个 status 字段，枚举为 loading, success, error，可根据其值来设置状态
    onCurQueueChange: null,
    // video-player 会同时渲染 3 个，当有 video 销毁时触发
    onVideoDestroy: null,
  };

  /**
   * 构造函数 - 初始化 VideoSwiper 实例
   * @param {Object} options 配置选项
   */
  constructor(options) {
    // 检查起始剧集是否有效
    this._checkStart(options.start, options.total);

    // 合并配置选项
    Object.assign(this.options, options);

    // 验证必要的选项是否提供
    if (!this.options.getVideo) {
      throw new Error('options.getVideo is required');
    }

    if (this.options.start + 1 > this.options.total || this.options.start < 0) {
      throw new Error('options.start is invalid');
    }

    if (!this.options.onSwiperOptionsChange) {
      throw new Error('options.onSwiperOptionsChange is required');
    }

    if (!this.options.onCurQueueChange) {
      throw new Error('options.onCurQueueChange is required');
    }

    // 检查平台特性支持
    const canUseBindGetsource = tt.canIUse('video-player.bindgetsource');
    this.canUseBindGetsource = canUseBindGetsource;

    const canUsePreloadResourceVideo = tt.canIUse('preloadResourceVideo');
    this.canUsePreloadResourceVideo = canUsePreloadResourceVideo;

    // 初始化视频播放器
    this._init();
  }

  /**
   * 触发 curQueue 变更的回调
   * 使用 Promise.resolve().then 防止频繁调用，确保在当前事件循环结束后执行
   */
  runningTrigger = false;
  _triggerCurQueueChange() {
    if (this.runningTrigger) {
      return;
    }

    this.runningTrigger = true;

    Promise.resolve().then(() => {
      this.options.onCurQueueChange(this.curQueue);
      this.runningTrigger = false;
    });
  }

  /**
   * 触发 swiperOptions 变更的回调
   */
  _triggerSwiperOptionsChange() {
    this.options.onSwiperOptionsChange(this.swiperOptions);
  }

  /**
   * 设置 swiperOptions 并触发变更回调
   * @param {Object} obj 新的 swiperOptions 属性
   */
  _setSwiperOptions(obj) {
    Object.assign(this.swiperOptions, obj);
    this._triggerSwiperOptionsChange();
  }

  /**
   * 设置指定 swiper 索引处的视频信息
   * 处理视频销毁逻辑，更新 curQueue
   * @param {number} swiperIndex swiper 项的索引
   * @param {Object} info 视频信息对象
   */
  _setCurQueue(swiperIndex, info) {
    const { curQueue } = this;
    const lastVideoInfo = curQueue[swiperIndex];

    // 如果之前有视频，且 ID 不同，则销毁旧视频
    if (
      lastVideoInfo &&
      lastVideoInfo.id !== undefined &&
      lastVideoInfo.id !== info?.id
    ) {
      this.hasGotSource[lastVideoInfo.episode] = false;
      this.cacheVideoContext[lastVideoInfo.id] = undefined;
      this.options.onVideoDestroy && this.options.onVideoDestroy(lastVideoInfo);
    }

    // 更新 curQueue 并触发变更
    curQueue[swiperIndex] = info;
    Object.assign(this.curQueue, curQueue);
    this._triggerCurQueueChange();
  }

  /**
   * 删除指定索引处的 swiper 项
   * 处理视频销毁逻辑，更新 curQueue
   * @param {number} index 要删除的索引
   */
  _deleteCurQueue(index) {
    console.log("🚀 ~ VideoSwiper ~ _deleteCurQueue ~ index:", index)
    const { curQueue } = this;
    const videoInfo = curQueue[index];

    // 如果有视频，则销毁
    if (videoInfo) {
      this.hasGotSource[videoInfo.episode] = false;
      this.cacheVideoContext[videoInfo.id] = undefined;
      this.options.onVideoDestroy && this.options.onVideoDestroy(videoInfo);
    }

    // 从数组中删除项
    curQueue.splice(index, 1);

    this.curQueue = curQueue;
    this._triggerCurQueueChange();
  }

  /**
   * 预加载视频资源并在完成后渲染
   * 使用 tt.preloadResourceVideo 接口预加载视频资源
   * @param {Object} videoInfo 视频信息
   * @param {Function} setCb 回调函数，接收预加载状态
   */
  _renderVideoAfterPreload(videoInfo, setCb) {
    if (!this.canUsePreloadResourceVideo) {
      setCb('fail');
      return;
    }

    const start = Date.now();
    tt.preloadResourceVideo({
      episodeId: videoInfo.eid,
      albumId: videoInfo.aid,
      cloudType: videoInfo.cloudType || 1,
      threePartyCloud: videoInfo.threePartyCloud,
      success() {
        setCb('success');
      },
      fail() {
        setCb('fail');
      },
      complete(res) {
        console.warn('preloadResourceVideo', res, Date.now() - start);
      },
    });
  }

  /**
   * 获取指定索引的视频信息
   * 设置加载状态，请求视频信息，更新视图
   * @param {number} index 视频索引
   * @param {number} swiperIndex swiper 项索引
   * @param {boolean} needRenderAfterPreload 是否需要预加载后渲染
   */
  async _getVideo(index, swiperIndex, needRenderAfterPreload = true) {
    let info = {
      status: 'loading',
    };

    const setVideoInfo = (videoInfo) => {
      this.videoList[index] = videoInfo;
      this._setCurQueue(swiperIndex, videoInfo);
    };

    try {
      // 先设置加载状态
      setVideoInfo(info);

      // 请求视频信息
      const res = await this.options.getVideo(index);

      info = {
        ...res,
        status: 'success',
      };

      if (needRenderAfterPreload) {
        // 预加载视频资源
        this._renderVideoAfterPreload(res, (status) => {
          if (status === 'success') {
            setVideoInfo({
              ...info,
              // 预加载成功时，移除封面图，直接显示首帧
              episodes_cover: undefined,
            });
            return;
          }
          setVideoInfo(info);
        });
      } else {
        setVideoInfo(info);
      }
    } catch (error) {
      console.error(error);
      info = {
        status: 'error',
      };
      setVideoInfo(info);
    }
  }

  /**
   * 计算初始 swiper 位置
   * 确保第一个视频始终在位置0
   * @returns {number} 初始 swiper 索引
   */
  _initSwiperCurrent() {
    // 初始位置需要是除以 3 的余数
    // 确保向上滑动时，第一个视频永远在第一个 swiper-item 中
    return this.options.start % 3;
  }

  /**
   * 初始化时预加载视频
   * 根据初始位置预加载上一集或下一集
   * @param {number} current 当前 swiper 索引
   */
  _initPreload(current) {
    switch (current) {
      case 0:
        // 位置0：预加载下一集
        this._preload(1, 'down');
        break;
      case 1:
        // 位置1：预加载上一集和下一集
        this._preload(1, 'down');
        this._preload(1, 'up');
        break;
      case 2:
        // 位置2：预加载上一集
        this._preload(1, 'up');
        break;
      default:
        break;
    }
  }

  /**
   * 初始化 VideoSwiper
   * 设置初始状态，获取并显示初始视频
   */
  async _init() {
    // 根据总集数设置 curQueue 大小
    if (this.options.total <= 3) {
      this.curQueue = new Array(this.options.total).fill(null);
    } else {
      this.curQueue = new Array(3).fill(null);
    }

    // 计算当前集索引
    this._changeCurrentIndex(this.options.start);

    // 计算初始 swiper 位置
    const swiperCurrent = this._initSwiperCurrent();

    // 判断是否需要获取视频信息
    if (this._checkNeedFetchVideo(this.currentIndex)) {
      this._getVideo(this.currentIndex, swiperCurrent, false);
    } else { // 如果不需要获取视频信息，则直接设置当前视频信息
      const videoInfo = this.videoList[this.currentIndex];
      this._setCurQueue(swiperCurrent, videoInfo);
    }

    // 设置 swiper 选项并预加载相邻视频
    this._setSwiperOptions({
      current: swiperCurrent,
    });

    this._initPreload(swiperCurrent);
  }

  /**
   * 检查是否需要获取视频信息
   * @param {number} index 视频索引
   * @returns {boolean} 是否需要获取
   */
  _checkNeedFetchVideo(index) {
    return !this.videoList[index] || this.videoList[index].status === 'error';
  }

  /**
   * 检查上一个索引是否有效
   * @param {number} preIndex 上一个索引
   * @returns {boolean} 是否有效
   */
  _checkPreIndexValid(preIndex) {
    return preIndex >= 0;
  }

  /**
   * 检查下一个索引是否有效
   * @param {number} nextIndex 下一个索引
   * @returns {boolean} 是否有效
   */
  _checkNextIndexValid(nextIndex) {
    return nextIndex < this.options.total;
  }

  /**
   * 检查是否可以循环滑动
   * 第一集和最后一集不允许循环
   * @returns {boolean} 是否可以循环
   */
  _checkCanCircular() {
    return (
      this.currentIndex !== 0 && this.currentIndex !== this.options.total - 1
    );
  }

  /**
   * 检查起始剧集是否有效
   * @param {number} start 起始索引
   * @param {number} total 总集数
   */
  _checkStart(start, total) {
    if (start + 1 > total || start < 0) {
      throw new Error('options.start is invalid');
    }
  }

  /**
   * 转换实际索引到 swiper 索引
   * 由于 swiper-item 只有3个，需要映射实际剧集索引
   * @param {number} index 实际索引
   * @returns {number} swiper 索引
   */
  _formatSwiperIndex(index) {
    if (index < 0) {
      const x = index % 3;

      if (x === 0) {
        return 0;
      }

      return 3 + x;
    }

    return index % 3;
  }

  /**
   * 设置当前播放的集数，并更新 preIndex 和 nextIndex
   * @param {number} targetIndex 目标索引
   */
  _changeCurrentIndex(targetIndex) {
    this.currentIndex = targetIndex;
    this.preIndex = targetIndex - 1;
    this.nextIndex = targetIndex + 1;
  }

  /**
   * 预加载视频
   * 向前或向后预加载指定数量的视频
   * @param {number} count 预加载数量
   * @param {string} direction 方向，'up' 或 'down'
   */
  async _preload(count, direction) {
    const { current } = this.swiperOptions;

    // 根据滑动方向预加载
    if (direction === 'down') {
      // 向下滑动预加载
      for (let index = 1; index <= count; index++) {
        const nextIndex = this.currentIndex + index;
        const nextSwiperIndex = current + index;
        let normalizedNextSwiperIndex =
          this._formatSwiperIndex(nextSwiperIndex);

        if (this._checkNextIndexValid(nextIndex)) {
          // 特殊处理：最后一集且当前在位置2
          if (nextIndex === this.options.total - 1 && current === 2) {
            normalizedNextSwiperIndex = 3;
          }

          // 获取或预加载视频
          if (this._checkNeedFetchVideo(nextIndex)) {
            this._getVideo(nextIndex, normalizedNextSwiperIndex);
          } else {
            const videoInfo = this.videoList[nextIndex];

            this._renderVideoAfterPreload(videoInfo, (status) => {
              if (status === 'success') {
                this._setCurQueue(normalizedNextSwiperIndex, {
                  ...videoInfo,
                  episodes_cover: undefined,
                });
                return;
              }
              this._setCurQueue(normalizedNextSwiperIndex, videoInfo);
            });
          }
        }
      }
    } else if (direction === 'up') {
      // 向上滑动预加载
      for (let index = 1; index <= count; index++) {
        const preIndex = this.currentIndex - index;
        const preSwiperIndex = current - index;
        const normalizePreSwiperIndex = this._formatSwiperIndex(preSwiperIndex);

        if (this._checkPreIndexValid(preIndex)) {
          if (this._checkNeedFetchVideo(preIndex)) {
            this._getVideo(preIndex, normalizePreSwiperIndex);
          } else {
            const videoInfo = this.videoList[preIndex];

            this._renderVideoAfterPreload(videoInfo, (status) => {
              if (status === 'success') {
                this._setCurQueue(normalizePreSwiperIndex, {
                  ...videoInfo,
                  episodes_cover: undefined,
                });
                return;
              }
              this._setCurQueue(normalizePreSwiperIndex, videoInfo);
            });
          }
        }
      }
    }
  }

  /**
   * 切换当前播放的视频
   * 处理视频暂停、播放、队列调整等逻辑
   * @param {number} swiperIndex swiper 索引
   * @param {number} targetIndex 目标视频索引
   * @param {string} direction 方向，'up' 或 'down'
   */
  async _changeCurrent(swiperIndex, targetIndex, direction) {
    // 边界检查
    if (targetIndex < 0 || targetIndex >= this.options.total) {
      return;
    }

    // 暂停当前播放的视频
    this.pause();

    // 更新索引
    this._changeCurrentIndex(targetIndex);

    // 更新 swiper 配置
    this._setSwiperOptions({
      current: swiperIndex,
      circular: this._checkCanCircular(),
    });

    // 自动播放新视频
    if (this.canUseBindGetsource) {
      if (this.hasGotSource[targetIndex]) {
        this.play();
      }
    } else {
      // 兼容处理，延迟播放
      setTimeout(() => {
        this.play();
      }, 800);
    }

    const { curQueue } = this;

    // 特殊情况处理：
    // 1. 最后一集且 swiper 在位置1，删除位置2的视频
    if (targetIndex === this.options.total - 1 && swiperIndex === 1) {
      this._deleteCurQueue(2);
    } 
    // 2. 倒数第三集且当前有4个swiper项，删除最后一项
    else if (
      targetIndex === this.options.total - 3 &&
      curQueue.length === 4
    ) {
      this._deleteCurQueue(3);
    } 
    // 正常情况，预加载下一个方向的视频
    else {
      this._preload(1, direction);
    }
  }

  /**
   * 设置起始播放集数
   * @param {number} start 起始索引
   */
  setStart(start) {
    this._checkStart(start, this.options.total);
    this.options.start = start;
    this._init();
  }

  /**
   * 更新当前播放的视频信息
   * 重新获取当前视频的最新信息
   */
  async updateCurrentVideo() {
    const { currentIndex } = this;
    const currentSwiperIndex = this.swiperOptions.current;

    const setVideoInfo = (videoInfo) => {
      this.videoList[currentIndex] = videoInfo;
      this._setCurQueue(currentSwiperIndex, videoInfo);
    };

    const res = await this.options.getVideo(currentIndex);

    setVideoInfo({
      ...res,
      status: 'success',
    });
  }

  /**
   * 获取当前播放的视频信息
   * @returns {Object} 当前视频信息
   */
  getCurrentVideo() {
    return this.videoList[this.currentIndex];
  }

  /**
   * 获取当前视频的上下文
   * 用于控制视频播放、暂停等操作
   * @returns {Object} 视频上下文
   */
  getVideoContext() {
    const id = this.videoList[this.currentIndex]?.id;

    if (id === undefined) {
      return;
    }

    if (this.cacheVideoContext[id]) {
      return this.cacheVideoContext[id];
    }

    // 创建并缓存视频上下文
    const context = tt.createVideoContext(`${id}`);
    this.cacheVideoContext[id] = context;

    return context;
  }

  /**
   * 播放当前视频
   */
  play() {
    this.getVideoContext()?.play();
  }

  /**
   * 暂停当前视频
   */
  pause() {
    this.getVideoContext()?.pause();
  }

  /**
   * 处理 swiper 滑动事件
   * 根据滑动方向切换视频
   * @param {Object} e 事件对象
   */
  onchange(e) {
    const { current } = e.detail;
    const lastIndex = this.swiperOptions.current;
    const diff = current - lastIndex;
    const direction = diff === 1 || diff === -2 ? 'down' : 'up';

    console.warn('direction', direction, diff);

    if (lastIndex === current) {
      return;
    }

    let targetIndex = this.currentIndex;

    // 根据方向设置目标索引
    if (direction === 'down') {
      targetIndex = this.nextIndex;
    } else {
      targetIndex = this.preIndex;
    }

    // 切换到目标视频
    this._changeCurrent(current, targetIndex, direction);
  }

  /**
   * 处理视频资源加载完成事件
   * 用于自动播放和状态管理
   * @param {Object} e 事件对象
   */
  onGetSource(e) {
    const { episode } = e.target.dataset;
    
    const currentVideo = this.videoList[this.currentIndex];

    // 如果加载完成的是当前播放的视频，则自动播放
    if (currentVideo?.episode === episode) {
      this.play();
    }

    // 标记视频已加载完成
    this.hasGotSource[episode] = true;
  }

  /**
   * 处理视频播放结束事件
   * 自动切换到下一集或显示结束提示
   */
  onEnded() {
    const { nextIndex } = this;
    const { current } = this.swiperOptions;

    // 如果有下一集，自动切换
    if (this._checkNextIndexValid(nextIndex)) {
      this._changeCurrent(current + 1, nextIndex, 'down');
    } 
    // 否则显示结束提示
    else if (this.options.onEndList) {
      this.options.onEndList();
    }
  }
}
